// ValueModifier.cs
//
// Copyright (c) 2007 Mike Strobel
//
// This source code is subject to the terms of the Microsoft Reciprocal License (Ms-RL).
// For details, see <http://www.opensource.org/licenses/ms-rl.html>.
//
// All other rights reserved.

using System;
using System.Linq.Expressions;

namespace Supremacy.Types
{
    [Serializable]
    public class ValueModifier : ValueModifier<int> {}

    [Serializable]
    public class ValueModifier<T> where T : IConvertible
    {
        protected const byte CompoundMultiplierFlag = (1 << 0);
        protected const byte ApplyOffsetFirstFlag = (1 << 1);

        private static readonly Func<T, T, Percentage, T> s_compoundMultiplyExpression;
        private static readonly Func<T, T, Percentage, T> s_standardMultiplyExpression;
        private static readonly Func<T, T, int, T> s_offsetExpression;

        private byte _flags;
        private int _offset;
        private Percentage _multiplier;

        static ValueModifier()
        {
            var originalValue = Expression.Parameter(typeof(T), "originalValue");
            var currentValue = Expression.Parameter(typeof(T), "currentValue");
            var multiplier = Expression.Parameter(typeof(Percentage), "multiplier");
            var offset = Expression.Parameter(typeof(int), "offset");

            s_compoundMultiplyExpression = Expression.Lambda<Func<T, T, Percentage, T>>(
                Expression.Convert(
                    Expression.Multiply(
                        Expression.Convert(currentValue, typeof(decimal)),
                        Expression.Convert(multiplier, typeof(decimal))),
                    typeof(T)),
                originalValue,
                currentValue,
                multiplier).Compile();

            s_standardMultiplyExpression = Expression.Lambda<Func<T, T, Percentage, T>>(
                Expression.Convert(
                    Expression.Add(
                        Expression.Convert(currentValue, typeof(decimal)),
                        Expression.Subtract(
                            Expression.Multiply(
                                Expression.Convert(originalValue, typeof(decimal)),
                                Expression.Convert(multiplier, typeof(decimal))),
                            Expression.Convert(originalValue, typeof(decimal)))),
                    typeof(T)),
                originalValue,
                currentValue,
                multiplier).Compile();

            s_offsetExpression = Expression.Lambda<Func<T, T, int, T>>(
                Expression.Convert(
                    Expression.Add(
                        Expression.Convert(currentValue, typeof(decimal)),
                        Expression.Convert(offset, typeof(decimal))),
                    typeof(T)),
                originalValue,
                currentValue,
                offset).Compile();
        }

        protected void SetFlag(byte flag, bool value)
        {
            if (value)
                _flags |= flag;
            else
                _flags &= (byte)(~flag);
        }

        protected bool IsFlagSet(byte flag)
        {
            return ((_flags & flag) == flag);
        }

        public bool HasCompoundMultiplier
        {
            get { return IsFlagSet(CompoundMultiplierFlag); }
            set { SetFlag(CompoundMultiplierFlag, value); }
        }

        public bool IsOffsetAppliedFirst
        {
            get { return IsFlagSet(ApplyOffsetFirstFlag); }
            set { SetFlag(ApplyOffsetFirstFlag, value); }
        }

        public int Offset
        {
            get { return _offset; }
            set { _offset = value; }
        }

        public Percentage Multiplier
        {
            get { return _multiplier; }
            set { _multiplier = value; }
        }

        public T Apply(T originalValue)
        {
            return Apply(originalValue, originalValue);
        }

        public T Apply(T originalValue, T currentValue)
        {
            if (IsOffsetAppliedFirst)
                return ApplyMultiplier(originalValue, ApplyOffset(originalValue, currentValue));
            return ApplyOffset(originalValue, ApplyMultiplier(originalValue, currentValue));
        }

        protected T ApplyOffset(T originalValue, T currentValue)
        {
            return s_offsetExpression(originalValue, currentValue, _offset);
        }

        protected T ApplyMultiplier(T originalValue, T currentValue)
        {
            if (HasCompoundMultiplier)
                s_compoundMultiplyExpression(originalValue, currentValue, _multiplier);
            return s_standardMultiplyExpression(originalValue, currentValue, _multiplier);
        }

        public ValueModifier() : this(0, 1.0f) { }

        public ValueModifier(int offset, Percentage efficiency)
        {
            this._offset = offset;
            this._multiplier = efficiency;
        }
    }
}